﻿#region

using Mapster;
using Microsoft.Extensions.Options;
using Vin.Extension.Framework.Aop;
using Vin.Extension.Framework.Base;
using Vin.Extension.Framework.Extension;
using Vin.Extension.Framework.Monitor.Dto.Entities;
using Vin.Extension.Framework.Monitor.Dto.List;
using Vin.Extension.Framework.Monitor.Entities;
using Vin.Extension.Framework.Monitor.Options;
using Vin.Extension.Framework.Monitor.Repository;
using Vin.Extension.Framework.Monitor.ServiceInterface;
using Vin.Extension.Redis.Cache;
using Vin.Tool.AspNetCore.Service;
using Vin.Tool.AspNetCore.Singleton;
using Vin.Tool.Attributes;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Core.DateTimeCategory;
using Vin.Tool.Domain.ExceptionModel;

#endregion

namespace Vin.Extension.Framework.Monitor.Service;

public class SysLoginLogService : VinFrameworkBaseService, ISysLoginLogService
{
    [Inject] private readonly IOptionsMonitor<MonitorOption> _optionsMonitor = null!;
    private MonitorOption Option => _optionsMonitor.CurrentValue;
    
    [Inject] private readonly SysLoginLogRepository _sysLoginLogRepository = null!;

    private string _loginLogIdPrefix = "LoginLog:";
    private int _loginLogIdExpire = 5;

    public async Task<List<SysLoginLogDto>> ListAsync(SysLoginLogListDto dto)
    {
        dto.StartTime ??= dto.StartTime.ModifiedDateOfNull(-Option.DefaultQueryDays);
        dto.EndTime ??= dto.EndTime.ModifiedDateOfNull();
        if (dto.EndTime!.Value.Subtract(dto.StartTime!.Value).TotalDays > Option.DefaultQueryIntervalDays)
        {
            throw new BusinessException($"查询时间间隔不能超过{Option.DefaultQueryIntervalDays}天");
        }

        return (await _sysLoginLogRepository.AsQueryable()
                .Where(x => x.Type == dto.Type)
                .WhereIF(dto.Account.IsNotNullOrWhiteSpace(), x => x.Account == dto.Account)
                .WhereIF(dto.AccountId.HasValue, x => x.AccountId == dto.AccountId)
                .WhereIF(dto.UserId.HasValue, x => x.UserId == dto.UserId)
                .WhereIF(dto.Keyword.IsNotNullOrEmpty(),
                    x => x.Account!.Contains(dto.Keyword!) || x.UserName!.Contains(dto.Keyword!))
                .ToPageListAsync(dto))
            .Adapt<List<SysLoginLogDto>>();
    }

    /// <summary>
    /// 添加登录日志
    /// </summary>
    [VinFrameworkTransaction]
    public async Task AddLoginLogAsync(string type, long? accountId, string? account, bool isSuccess, string? msg,
        string? loginType, object? loginInfo, long? userId, string? userName)
    {
        var httpContextInfo = VinApp.HttpContextInfo();
        var log = new SysLoginLog()
        {
            Id = _sysLoginLogRepository.GetNextId(),
            Type = type,
            AccountId = accountId,
            Account = account,
            UserId = userId,
            UserName = userName,
            LoginType = loginType,
            LoginInfo = loginInfo,
            Status = isSuccess ? LoginStatus.Success : LoginStatus.Fail,
            Ip = httpContextInfo.Ip,
            Location = httpContextInfo.Location,
            Browser = httpContextInfo.Browser,
            Os = httpContextInfo.Os,
            UserAgent = httpContextInfo.UserAgent ?? "",
            Msg = msg,
        };

        await _sysLoginLogRepository.AsInsertable(log).IgnoreColumnsNull().ExecuteCommandAsync();

        if (isSuccess && userId == null)
        {
            RedisCacheManager.GetRedisCache()
                .SetCache(_loginLogIdPrefix + accountId, log.Id, TimeSpan.FromMinutes(_loginLogIdExpire));
        }
    }

    /// <summary>
    /// 更新登录日志
    /// </summary>
    public async Task UpdateLoginLogAsync(long? userId = null, string? userName = null)
    {
        var tokenModel = GetVinTokenModelOrNull();
        if (tokenModel == null) return;
        var logId = RedisCacheManager.GetRedisCache().GetCache<long?>(_loginLogIdPrefix + tokenModel.RealAccountId);
        if (logId == null) return;

        await _sysLoginLogRepository.UpdateAsync(x => new SysLoginLog()
        {
            UserId = userId,
            UserName = userName,
            Browser = tokenModel.Browser,
            Os = tokenModel.Os,
            UserAgent = tokenModel.UserAgent,
        }, x => x.Id == logId);

        await RedisCacheManager.GetRedisCache().RemoveCacheAsync(_loginLogIdPrefix + AccountId);
    }
}